#ifndef HTTPCONNECTION_H
#define HTTPCONNECTION_H

#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/uio.h>
#include <map>

#include "locker.h"
#include "sql_connection_pool.h"
#include "list_timer.h"
#include "log.h"
class http_conn{
public:
    //文件名的最大长度
    static const int FILENAME_LEN = 200;
    //读缓冲区的大小2M
    static const int READ_BUFFER_SIZE = 2048;
    //写缓冲的大小1M
    static const int WRITE_BUFFER_SIZE = 1024;
    //http的请求方法，仅支持get
    enum METHOD { GET = 0, POST, HEAD, PUT, DELETE, TRACE, OPTIONS, CONNECT, PATCH };
    /*主状态机的两种状态：
        分析请求行，
        分析请求头部字段,
        CHECK_STATE_CONTENT，解析消息体，仅用于解析POST请求
    */
    enum CHECK_STATE { CHECK_STATE_REQUESTLINE = 0, CHECK_STATE_HEADER, CHECK_STATE_CONTENT };
    /*服务器处理http请求结果：
        no_request 请求不完整，需要继续读取
        get_request获得一个完整的客户请求
        bad_request客户请求语法有错
        forbidden_request客户对资源没有权限
        internal_error服务器内部错误
        closed_connection客户端已关闭*/
    enum HTTP_CODE { NO_REQUSET, GET_REQUEST, BAD_REQUEST, NO_RESOURCE, FORBIDDEN_REQUEST,FILE_REQUEST,INTERNAL_ERROR, CLOSED_CONNECTION };
    /*从状态机的三种状态：读取到完整的行，行出错，行数据暂且不完整*/
    enum LINE_STATUS { LINE_OK = 0, LINE_BAD, LINE_OPEN };

public:
    http_conn(){};
    ~http_conn(){};

public:
    //初始化新接收的连接
    void init( int sockfd, const sockaddr_in& addr, char *, int, int, std::string user, std::string passwd, std::string sqlname );
    //关闭连接
    void close_conn( bool real_close = true );
    //处理客户请求
    void process();
    //非阻塞读操作
    bool read_once();
    //非阻塞写操作
    bool write();
    sockaddr_in *get_address(){
        return &m_address;
    }
    void initmysql_result(connection_pool *connPool);
    int timer_flag;
    int improv;

private:
    //初始化连接
    void init();
    //解析http请求
    HTTP_CODE process_read();
    //填充HTTP应答
    bool process_write( HTTP_CODE ret );

    //下面的函数用于process_read调用分析HTTP请求
    HTTP_CODE parse_request_line( char* text );
    HTTP_CODE parse_headers( char* text );
    HTTP_CODE parse_content( char* text );
    HTTP_CODE do_request();
    char* get_line(){
        return m_read_buf + m_start_line;
    }
    LINE_STATUS parse_line();

    //下面函数process_write调用分析HTTP应答
    void unmap();
    bool add_response( const char* format, ...);
    bool add_content( const char* content );
    bool add_status_line( int statue, const char* title );
    bool add_headers( int content_length );
    bool add_content_type();
    bool add_content_length( int content_length );
    bool add_linger();
    bool add_blank_line();

public:
    /*当我们声明类的成员为静态时，这意味着无论创建多少个类的对象，静态成员都只有一个副本。*/
    /*epoll文件描述符是静态的，因为所有事件都注册到一个epoll内核事件中*/
    static int m_epollfd;
    /*用户数量*/
    static int m_user_count;
    MYSQL *mysql;
    int m_state;  //读为0, 写为1

private:
    //该http连接的socket和对方socket地址
    int m_sockfd;
    sockaddr_in m_address;

    //读缓冲区
    char m_read_buf [ READ_BUFFER_SIZE ];
    //标识缓冲区内已经读入的客户数据的最后一个字节的下一个位置
    int m_read_idx;
    //当前正在分析的字符在读缓冲区的位置
    int m_checked_idx;
    //当前正在解析的行的起始位置
    int m_start_line;
    //写缓冲区
    char m_write_buf [ WRITE_BUFFER_SIZE ];
    //写缓冲中待发送的字节数
    int m_write_idx;

    //主状态机当前所处的状态
    CHECK_STATE m_check_state;
    //请求方法
    METHOD m_method;

    //客户请求的目标文件夹的完整路径，其内容等于doc_root+m_url,doc_root是网站的根目录
    char m_real_file [ FILENAME_LEN ];
    //客户请求的目标文件夹的文件名
    char* m_url;
    //http协议版本号，我们只支持http/1.1
    char* m_version;
    //主机名
    char* m_host;
    //http请求的消息体的长度
    long m_content_length;
    //http请求是否要求保持连接
    bool m_linger;

    //客户请求的目标文件被mmap到内存的起始位置
    char* m_file_address;
    //目标文件的状态，判断文件存在，为目录，可靠，并获取文件大小
    struct stat m_file_stat;
    //我们将用writev执行写操作
    struct iovec m_iv[2];
    int m_iv_count;//内存块的数量
    int cgi;        //是否启用的POST
    char *m_string; //存储请求头数据
    int bytes_to_send;
    int bytes_have_send;
    char *doc_root;

    std::map<std::string, std::string> m_users;
    int m_TRIGMode;
    int m_close_log;

    char sql_user[100];
    char sql_passwd[100];
    char sql_name[100];
};




#endif // !HTTPCONNECTION_H